Kuasai Resource Timing API untuk mendiagnosis dan mengoptimalkan performa frontend. Pelajari cara mengukur setiap waktu muat sumber daya, dari pencarian DNS hingga pengunduhan konten.
Mengungkap Performa Frontend: Analisis Mendalam Resource Timing API
Dalam dunia pengembangan web, kecepatan bukan hanya sekadar fitur; itu adalah persyaratan mendasar untuk pengalaman pengguna yang positif. Situs web yang lambat dimuat dapat menyebabkan rasio pentalan yang lebih tinggi, keterlibatan pengguna yang lebih rendah, dan pada akhirnya, dampak negatif pada tujuan bisnis. Meskipun alat seperti Lighthouse dan WebPageTest memberikan diagnostik tingkat tinggi yang tak ternilai, mereka sering kali mewakili satu pengujian sintetis. Untuk benar-benar memahami dan mengoptimalkan performa bagi audiens global, kita perlu mengukur pengalaman pengguna nyata, di perangkat mereka, di jaringan mereka. Di sinilah Real User Monitoring (RUM) berperan, dan salah satu alatnya yang paling kuat adalah Resource Timing API.
Panduan komprehensif ini akan membawa Anda menyelam lebih dalam ke Resource Timing API. Kita akan menjelajahi apa itu, bagaimana menggunakannya, dan bagaimana mengubah data granularnya menjadi wawasan yang dapat ditindaklanjuti yang dapat secara dramatis meningkatkan performa pemuatan aplikasi Anda. Baik Anda seorang insinyur frontend berpengalaman atau baru memulai perjalanan optimisasi performa, artikel ini akan membekali Anda dengan pengetahuan untuk membedah dan memahami performa jaringan dari setiap aset di halaman Anda.
Apa itu Resource Timing API?
Resource Timing API adalah API JavaScript berbasis browser yang menyediakan data waktu jaringan terperinci untuk setiap sumber daya yang diunduh oleh halaman web. Anggap saja sebagai lensa mikroskopis untuk aktivitas jaringan halaman Anda. Untuk setiap gambar, skrip, stylesheet, font, dan panggilan API (melalui `fetch` atau `XMLHttpRequest`), API ini menangkap stempel waktu beresolusi tinggi untuk setiap tahap permintaan jaringan.
Ini adalah bagian dari serangkaian Performance API yang lebih besar, yang bekerja sama untuk memberikan pandangan holistik tentang performa aplikasi Anda. Sementara Navigation Timing API berfokus pada siklus hidup dokumen utama, Resource Timing API memperbesar semua sumber daya dependen yang diminta oleh dokumen utama.
Mengapa API ini Sangat Penting?
- Granularitas: Ini melampaui metrik tunggal "waktu muat halaman". Anda dapat melihat dengan tepat berapa lama pencarian DNS, koneksi TCP, dan pengunduhan konten berlangsung untuk skrip pihak ketiga tertentu atau gambar hero yang kritis.
- Data Pengguna Nyata: Tidak seperti alat berbasis lab, API ini berjalan di browser pengguna Anda. Ini memungkinkan Anda mengumpulkan data performa dari berbagai kondisi jaringan, perangkat, dan lokasi geografis, memberi Anda gambaran nyata tentang pengalaman pengguna global Anda.
- Wawasan yang Dapat Ditindaklanjuti: Dengan menganalisis data ini, Anda dapat menunjukkan hambatan spesifik. Apakah skrip analitik pihak ketiga lambat untuk terhubung? Apakah CDN Anda berkinerja buruk di wilayah tertentu? Apakah gambar Anda terlalu besar? Resource Timing API memberikan bukti yang dibutuhkan untuk menjawab pertanyaan-pertanyaan ini dengan percaya diri.
Anatomi Pemuatan Sumber Daya: Membedah Linimasa
Inti dari Resource Timing API adalah objek `PerformanceResourceTiming`. Untuk setiap sumber daya yang dimuat, browser membuat salah satu objek ini, yang berisi banyak informasi waktu dan ukuran. Untuk memahami objek-objek ini, akan sangat membantu jika memvisualisasikan proses pemuatan sebagai diagram air terjun (waterfall chart), di mana setiap langkah mengikuti langkah sebelumnya.
Mari kita uraikan properti kunci dari objek `PerformanceResourceTiming`. Semua nilai waktu adalah stempel waktu beresolusi tinggi yang diukur dalam milidetik dari awal navigasi halaman (`performance.timeOrigin`).
startTime -> fetchStart -> domainLookupStart -> domainLookupEnd -> connectStart -> connectEnd -> requestStart -> responseStart -> responseEnd
Properti Waktu Kunci
name: URL dari sumber daya. Ini adalah pengidentifikasi utama Anda.entryType: String yang menunjukkan jenis entri performa. Untuk tujuan kita, ini akan selalu menjadi "resource".initiatorType: Ini sangat berguna untuk debugging. Ini memberitahu Anda bagaimana sumber daya itu diminta. Nilai umum termasuk 'img', 'link' (untuk CSS), 'script', 'css' (untuk sumber daya yang dimuat dari dalam CSS seperti `@import`), 'fetch', dan 'xmlhttprequest'.duration: Total waktu yang dibutuhkan untuk sumber daya, dihitung sebagairesponseEnd - startTime. Ini adalah metrik tingkat atas untuk satu sumber daya.startTime: Stempel waktu tepat sebelum pengambilan sumber daya dimulai.fetchStart: Stempel waktu tepat sebelum browser mulai mengambil sumber daya. Browser mungkin memeriksa cache (cache HTTP, cache Service Worker) sebelum melanjutkan ke jaringan. Jika sumber daya disajikan dari cache, banyak nilai waktu berikutnya akan menjadi nol.domainLookupStart&domainLookupEnd: Ini menandai awal dan akhir dari pencarian DNS (Domain Name System). Durasinya (domainLookupEnd - domainLookupStart) adalah waktu yang dibutuhkan untuk menyelesaikan nama domain menjadi alamat IP. Nilai yang tinggi di sini mungkin menunjukkan penyedia DNS yang lambat.connectStart&connectEnd: Ini menandai awal dan akhir pembentukan koneksi ke server. Untuk HTTP, ini adalah jabat tangan tiga arah TCP. Durasinya (connectEnd - connectStart) adalah waktu koneksi TCP Anda.secureConnectionStart: Jika sumber daya dimuat melalui HTTPS, stempel waktu ini menandai dimulainya jabat tangan SSL/TLS. Durasinya (connectEnd - secureConnectionStart) memberitahu Anda berapa lama negosiasi enkripsi berlangsung. Jabat tangan TLS yang lambat bisa menjadi tanda kesalahan konfigurasi server atau latensi jaringan.requestStart: Stempel waktu tepat sebelum browser mengirim permintaan HTTP yang sebenarnya untuk sumber daya ke server. Waktu antaraconnectEnddanrequestStartsering disebut waktu "antrian permintaan", di mana browser sedang menunggu koneksi yang tersedia.responseStart: Stempel waktu ketika browser menerima byte pertama dari respons dari server. Durasinya (responseStart - requestStart) adalah Time to First Byte (TTFB) yang terkenal. TTFB yang tinggi hampir selalu merupakan indikator dari proses backend yang lambat atau latensi di sisi server.responseEnd: Stempel waktu ketika byte terakhir dari sumber daya telah diterima, berhasil menutup permintaan. Durasinya (responseEnd - responseStart) mewakili waktu pengunduhan konten.
Properti Ukuran Sumber Daya
Memahami ukuran sumber daya sama pentingnya dengan memahami waktu. API ini menyediakan tiga metrik utama:
transferSize: Ukuran dalam byte dari sumber daya yang ditransfer melalui jaringan, termasuk header dan badan respons yang dikompresi. Jika sumber daya disajikan dari cache, ini sering kali akan menjadi 0. Ini adalah angka yang secara langsung memengaruhi paket data dan waktu jaringan pengguna.encodedBodySize: Ukuran dalam byte dari badan payload *setelah* kompresi (misalnya, Gzip atau Brotli) tetapi *sebelum* dekompresi. Ini membantu Anda memahami ukuran payload itu sendiri, terpisah dari header.decodedBodySize: Ukuran dalam byte dari badan payload dalam bentuk aslinya yang tidak terkompresi. Membandingkan ini denganencodedBodySizemengungkapkan efektivitas strategi kompresi Anda. Jika kedua angka ini sangat berdekatan untuk aset berbasis teks (seperti JS, CSS, atau HTML), kemungkinan kompresi Anda tidak berfungsi dengan benar.
Server Timing
Salah satu integrasi paling kuat dengan Resource Timing API adalah properti `serverTiming`. Backend Anda dapat mengirim metrik performa dalam header HTTP khusus (`Server-Timing`), dan metrik-metrik ini akan muncul dalam array `serverTiming` pada objek `PerformanceResourceTiming` yang sesuai. Ini menjembatani kesenjangan antara pemantauan performa frontend dan backend, memungkinkan Anda melihat waktu kueri basis data atau penundaan pemrosesan API langsung di data frontend Anda.
Sebagai contoh, backend dapat mengirim header ini:
Server-Timing: db;dur=53, api;dur=47.2, cache;desc="HIT"
Data ini akan tersedia di properti `serverTiming`, memungkinkan Anda untuk mengkorelasikan TTFB yang tinggi dengan proses lambat spesifik di backend.
Cara Mengakses Data Resource Timing di JavaScript
Sekarang setelah kita memahami data yang tersedia, mari kita lihat cara-cara praktis untuk mengumpulkannya menggunakan JavaScript. Ada dua metode utama.
Metode 1: `performance.getEntriesByType('resource')`
Ini adalah cara termudah untuk memulai. Metode ini mengembalikan sebuah array dari semua objek `PerformanceResourceTiming` untuk sumber daya yang telah selesai dimuat di halaman pada saat panggilan dilakukan.
// Tunggu hingga halaman dimuat untuk memastikan sebagian besar sumber daya ditangkap
window.addEventListener('load', () => {
const resources = performance.getEntriesByType('resource');
resources.forEach((resource) => {
console.log(`Sumber daya dimuat: ${resource.name}`);
console.log(` - Waktu Total: ${resource.duration.toFixed(2)}ms`);
console.log(` - Inisiator: ${resource.initiatorType}`);
console.log(` - Ukuran Transfer: ${resource.transferSize} bytes`);
});
});
Batasan: Metode ini adalah potret sesaat. Jika Anda memanggilnya terlalu dini, Anda akan kehilangan sumber daya yang belum dimuat. Jika aplikasi Anda secara dinamis memuat sumber daya jauh setelah pemuatan halaman awal, Anda perlu memanggil metode ini berulang kali, yang tidak efisien.
Metode 2: `PerformanceObserver` (Pendekatan yang Direkomendasikan)
`PerformanceObserver` adalah cara yang lebih modern, kuat, dan berkinerja untuk mengumpulkan entri performa. Alih-alih Anda melakukan polling untuk data, browser akan mendorong entri baru ke callback observer Anda saat entri tersebut tersedia.
Inilah mengapa ini lebih baik:
- Asinkron: Tidak memblokir thread utama.
- Komprehensif: Dapat menangkap entri dari awal pemuatan halaman, menghindari kondisi balapan di mana skrip berjalan setelah sumber daya sudah dimuat.
- Efisien: Menghindari kebutuhan untuk polling dengan `setTimeout` atau `setInterval`.
Berikut adalah implementasi standarnya:
try {
const observer = new PerformanceObserver((list) => {
list.getEntries().forEach((entry) => {
// Proses setiap entri sumber daya saat masuk
if (entry.entryType === 'resource') {
console.log(`Sumber daya diamati: ${entry.name}`);
console.log(` - Time to First Byte (TTFB): ${(entry.responseStart - entry.requestStart).toFixed(2)}ms`);
}
});
});
// Mulai mengamati entri 'resource'.
// Flag 'buffered' memastikan kita mendapatkan entri yang dimuat sebelum observer kita dibuat.
observer.observe({ type: 'resource', buffered: true });
// Anda dapat berhenti mengamati nanti jika diperlukan
// observer.disconnect();
} catch (e) {
console.error('PerformanceObserver tidak didukung di browser ini.');
}
Opsi buffered: true sangat penting. Ini memberitahu observer untuk segera mengirimkan semua entri `resource` yang sudah ada di buffer entri performa browser, memastikan Anda mendapatkan daftar lengkap dari awal.
Mengelola Buffer Performa
Browser memiliki batas default tentang berapa banyak entri waktu sumber daya yang mereka simpan (biasanya 150). Pada halaman yang sangat kompleks, buffer ini bisa penuh. Ketika itu terjadi, browser akan menembakkan event `resourcetimingbufferfull`, dan tidak ada entri baru yang ditambahkan.
Anda dapat mengelolanya dengan:
- Meningkatkan ukuran buffer: Gunakan `performance.setResourceTimingBufferSize(limit)` untuk menetapkan batas yang lebih tinggi, misalnya, 300.
- Membersihkan buffer: Gunakan `performance.clearResourceTimings()` setelah Anda memproses entri untuk memberi ruang bagi yang baru.
performance.addEventListener('resourcetimingbufferfull', () => {
console.warn('Buffer Resource Timing penuh. Membersihkan...');
// Proses entri yang ada dari observer Anda terlebih dahulu
// Kemudian bersihkan buffer
performance.clearResourceTimings();
// Anda mungkin perlu menyesuaikan kembali ukuran buffer jika ini sering terjadi
// performance.setResourceTimingBufferSize(500);
});
Kasus Penggunaan Praktis dan Wawasan yang Dapat Ditindaklanjuti
Mengumpulkan data hanyalah langkah pertama. Nilai sebenarnya terletak pada mengubah data itu menjadi perbaikan yang dapat ditindaklanjuti. Mari kita jelajahi beberapa masalah performa umum dan bagaimana Resource Timing API membantu Anda menyelesaikannya.
Kasus Penggunaan 1: Mengidentifikasi Skrip Pihak Ketiga yang Lambat
Masalah: Skrip pihak ketiga untuk analitik, periklanan, widget dukungan pelanggan, dan pengujian A/B terkenal sebagai pembunuh performa. Mereka bisa lambat dimuat, memblokir rendering, dan bahkan menyebabkan ketidakstabilan.
Solusi: Gunakan Resource Timing API untuk mengisolasi dan mengukur dampak skrip ini pada pengguna nyata Anda.
const observer = new PerformanceObserver((list) => {
const thirdPartyScripts = list.getEntries().filter(entry =>
entry.initiatorType === 'script' &&
!entry.name.startsWith(window.location.origin)
);
thirdPartyScripts.forEach(script => {
if (script.duration > 200) { // Tetapkan ambang batas, mis., 200ms
console.warn(`Skrip pihak ketiga yang lambat terdeteksi: ${script.name}`, {
duration: `${script.duration.toFixed(2)}ms`,
transferSize: `${script.transferSize} bytes`
});
// Di alat RUM sungguhan, Anda akan mengirim data ini ke backend analitik Anda.
}
});
});
observer.observe({ type: 'resource', buffered: true });
Wawasan yang Dapat Ditindaklanjuti:
- Durasi Tinggi: Jika sebuah skrip secara konsisten memiliki durasi yang panjang, pertimbangkan apakah itu benar-benar diperlukan. Dapatkah fungsionalitasnya diganti dengan alternatif yang lebih berkinerja?
- Strategi Pemuatan: Apakah skrip dimuat secara sinkron? Gunakan atribut `async` atau `defer` pada tag `<script>` untuk mencegahnya memblokir rendering halaman.
- Hosting Selektif: Dapatkah skrip dimuat secara kondisional, hanya pada halaman di mana itu benar-benar dibutuhkan?
Kasus Penggunaan 2: Mengoptimalkan Pengiriman Gambar
Masalah: Gambar besar yang tidak dioptimalkan adalah salah satu penyebab paling umum dari pemuatan halaman yang lambat, terutama pada perangkat seluler dengan bandwidth terbatas.
Solusi: Saring entri sumber daya berdasarkan `initiatorType: 'img'` dan analisis ukuran serta waktu muatnya.
// ... di dalam callback PerformanceObserver ...
list.getEntries()
.filter(entry => entry.initiatorType === 'img')
.forEach(image => {
const downloadTime = image.responseEnd - image.responseStart;
// Gambar besar mungkin memiliki waktu unduh yang tinggi dan transferSize yang besar
if (downloadTime > 500 || image.transferSize > 100000) { // 500ms atau 100KB
console.log(`Potensi masalah gambar besar: ${image.name}`, {
downloadTime: `${downloadTime.toFixed(2)}ms`,
transferSize: `${(image.transferSize / 1024).toFixed(2)} KB`
});
}
});
Wawasan yang Dapat Ditindaklanjuti:
- `transferSize` dan `downloadTime` Tinggi: Ini adalah sinyal jelas bahwa gambar terlalu besar. Optimalkan dengan menggunakan format modern seperti WebP atau AVIF, mengompresnya dengan tepat, dan mengubah ukurannya sesuai dimensi tampilannya.
- Gunakan `srcset`: Terapkan gambar responsif menggunakan atribut `srcset` untuk menyajikan ukuran gambar yang berbeda berdasarkan viewport pengguna.
- Lazy Loading: Untuk gambar di bawah lipatan (below the fold), gunakan `loading="lazy"` untuk menunda pemuatannya hingga pengguna menggulirnya ke dalam tampilan.
Kasus Penggunaan 3: Mendiagnosis Hambatan Jaringan
Masalah: Terkadang, masalahnya bukan pada sumber daya itu sendiri tetapi pada jalur jaringan ke sana. DNS yang lambat, koneksi laten, atau server yang kelebihan beban semuanya dapat menurunkan performa.
Solusi: Uraikan `duration` menjadi fase-fase komponennya untuk menunjukkan sumber penundaan.
function analyzeNetworkPhases(resource) {
const dnsTime = resource.domainLookupEnd - resource.domainLookupStart;
const tcpTime = resource.connectEnd - resource.connectStart;
const ttfb = resource.responseStart - resource.requestStart;
const downloadTime = resource.responseEnd - resource.responseStart;
console.log(`Analisis untuk ${resource.name}`);
if (dnsTime > 50) console.warn(` - Waktu DNS tinggi: ${dnsTime.toFixed(2)}ms`);
if (tcpTime > 100) console.warn(` - Waktu koneksi TCP tinggi: ${tcpTime.toFixed(2)}ms`);
if (ttfb > 300) console.warn(` - TTFB tinggi (server lambat): ${ttfb.toFixed(2)}ms`);
if (downloadTime > 500) console.warn(` - Pengunduhan konten lambat: ${downloadTime.toFixed(2)}ms`);
}
// ... panggil analyzeNetworkPhases(entry) di dalam observer Anda ...
Wawasan yang Dapat Ditindaklanjuti:
- Waktu DNS Tinggi: Penyedia DNS Anda mungkin lambat. Pertimbangkan untuk beralih ke penyedia global yang lebih cepat. Anda juga dapat menggunakan `` untuk menyelesaikan DNS untuk domain pihak ketiga yang penting sebelumnya.
- Waktu TCP Tinggi: Ini menunjukkan latensi dalam membangun koneksi. Content Delivery Network (CDN) dapat mengurangi ini dengan menyajikan aset dari lokasi yang secara geografis lebih dekat dengan pengguna. Menggunakan `` dapat melakukan pencarian DNS dan jabat tangan TCP lebih awal.
- TTFB Tinggi: Ini menunjuk ke backend yang lambat. Bekerja samalah dengan tim backend Anda untuk mengoptimalkan kueri basis data, meningkatkan caching sisi server, atau meningkatkan perangkat keras server. Header `Server-Timing` adalah sahabat terbaik Anda di sini.
- Waktu Unduh Tinggi: Ini adalah fungsi dari ukuran sumber daya dan bandwidth jaringan. Optimalkan aset (kompres, minifikasi) atau gunakan CDN untuk meningkatkan throughput.
Batasan dan Pertimbangan
Meskipun sangat kuat, Resource Timing API memiliki beberapa batasan penting yang perlu diperhatikan.
Sumber Daya Lintas-Asal dan Header `Timing-Allow-Origin`
Untuk alasan keamanan, browser membatasi detail waktu yang tersedia untuk sumber daya yang dimuat dari asal (domain, protokol, atau port) yang berbeda dari halaman utama Anda. Secara default, untuk sumber daya lintas-asal, sebagian besar properti waktu seperti `redirectStart`, `domainLookupStart`, `connectStart`, `requestStart`, `responseStart`, dan properti ukuran seperti `transferSize` akan menjadi nol.
Untuk mengekspos detail ini, server yang menghosting sumber daya harus menyertakan header HTTP `Timing-Allow-Origin` (TAO). Sebagai contoh:
Timing-Allow-Origin: * (Mengizinkan asal mana pun untuk melihat detail waktu)
Timing-Allow-Origin: https://www.your-website.com (Hanya mengizinkan situs web Anda)
Ini sangat penting saat bekerja dengan CDN atau API Anda sendiri di subdomain yang berbeda. Pastikan mereka dikonfigurasi untuk mengirim header TAO sehingga Anda bisa mendapatkan visibilitas performa penuh.
Dukungan Browser
Resource Timing API, termasuk `PerformanceObserver`, didukung secara luas di semua browser modern (Chrome, Firefox, Safari, Edge). Namun, untuk browser lama, mungkin tidak tersedia. Selalu bungkus kode Anda dalam blok `try...catch` atau periksa keberadaan `window.PerformanceObserver` sebelum menggunakannya untuk menghindari kesalahan pada klien lawas.
Kesimpulan: Dari Data Menjadi Keputusan
Resource Timing API adalah instrumen penting dalam perangkat pengembang web modern. Ini mengungkap misteri waterfall jaringan, menyediakan data mentah dan granular yang diperlukan untuk beralih dari keluhan samar "situs ini lambat" menjadi diagnosis berbasis data yang tepat seperti "widget obrolan pihak ketiga kami memiliki TTFB 400ms untuk pengguna di Asia Tenggara."
Dengan memanfaatkan `PerformanceObserver` untuk mengumpulkan data pengguna nyata dan menganalisis siklus hidup penuh setiap sumber daya, Anda dapat:
- Meminta pertanggungjawaban penyedia pihak ketiga atas performa mereka.
- Memvalidasi efektivitas CDN dan strategi caching Anda di seluruh dunia.
- Menemukan dan memperbaiki gambar berukuran besar dan aset yang tidak dioptimalkan.
- Mengkorelasikan penundaan frontend dengan waktu pemrosesan backend.
Perjalanan menuju web yang lebih cepat adalah proses yang berkelanjutan. Mulailah hari ini. Buka konsol pengembang browser Anda, jalankan cuplikan kode dari artikel ini di situs Anda sendiri, dan mulailah menjelajahi data performa kaya yang telah menunggu Anda selama ini. Dengan mengukur apa yang penting, Anda dapat membangun pengalaman yang lebih cepat, lebih tangguh, dan lebih menyenangkan bagi semua pengguna Anda, di mana pun mereka berada di dunia.